package game.breeze.socket.server;

import game.breeze.socket.connection.NIOConnection;
import game.breeze.socket.connection.NetConnection;
import game.breeze.socket.util.ByteBufferWrapper;
import game.breeze.socket.util.Utils;

import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;


public class NIONetServer extends NetServer implements Runnable {

	public NIONetServer() {
		data = new byte[1024];
		buffer = ByteBuffer.wrap(data, 0, data.length);
		connectionList = new ArrayList<NIOConnection>(1000);
	}

	public int getConnectionCount() {
		return connectionList.size();
	}

	public void run() {
		running = true;
		int result = 0;
		while (running) {
			long currentTime = System.currentTimeMillis();
			int size = connectionList.size();
			for (int i = 0; i < size; i++) {
				NIOConnection nioconnection = connectionList.get(i);
				if (nioconnection.isActive()) {
					nioconnection.idle(currentTime);
				} else {
					connectionClosed(nioconnection);
					connectionList.remove(i--);
					size--;
				}
			}

			try {
				result = selector.selectNow();
			} catch (IOException ioexception) {
				ioexception.printStackTrace();
			}
			if (result > 0)
				processSelection(currentTime);
			Utils.sleep(5);
		}
		int size = connectionList.size();
		for (int i = 0; i < size; i++) {
			NetConnection netconnection = connectionList.get(i);
			connectionClosed(netconnection);
			netconnection.close();
		}
	}

	public boolean start() {
		boolean flag = bindServer();
		if (!flag) {
			return false;
		} else {
			(new Thread(this, "NIONetServerThread")).start();
			return true;
		}
	}

	public void stop() {
		running = false;
	}

	private boolean bindServer() {
		try {
			serverChannel = ServerSocketChannel.open();
			serverSocket = serverChannel.socket();
			serverSocket.setReceiveBufferSize(1024);
			serverSocket.setPerformancePreferences(0, 2, 1);
			serverSocket.bind(new InetSocketAddress(getPort()));
			serverChannel.configureBlocking(false);
			selector = Selector.open();
			serverKey = serverChannel.register(selector, 16);
			return true;
		} catch (IOException ioexception) {
			ioexception.printStackTrace();
		}
		return false;
	}

	public static void main(String[] args) {
		System.out.println(SelectionKey.OP_ACCEPT);
		System.out.println(SelectionKey.OP_CONNECT);
		System.out.println(SelectionKey.OP_READ);
		System.out.println(SelectionKey.OP_WRITE);
	}

	private void processSelection(long currentTime) {
		Set<SelectionKey> set = selector.selectedKeys();
		Iterator<SelectionKey> iterator = set.iterator();
		while (true) {
			if (!iterator.hasNext())
				break;
			SelectionKey selectionkey = iterator.next();
			iterator.remove();
			if (selectionkey == serverKey) {
				if (selectionkey.isAcceptable()) {
					try {
						SocketChannel socketchannel = serverChannel.accept();
						socketchannel.configureBlocking(false);
						socketchannel.socket().setTcpNoDelay(false);
						SelectionKey selectionkey1 = socketchannel.register(selector, 1);
						NIOConnection nioconnection = new NIOConnection(socketchannel, this, new ByteBufferWrapper());
						// nioconnection.setCreatedTime(System.currentTimeMillis());
						selectionkey1.attach(nioconnection);
						connectionList.add(nioconnection);
						connectionOpened(nioconnection);
					} catch (IOException ioexception) {
						ioexception.printStackTrace();
					}
				}
			} else if (!selectionkey.isValid()) {
				NIOConnection nioconnection = (NIOConnection) selectionkey.attachment();
				if (nioconnection != null)
					nioconnection.onClosed();
			} else if (selectionkey.isReadable()) {
				NIOConnection nioconnection = (NIOConnection) selectionkey.attachment();
				try {
					SocketChannel socketchannel = (SocketChannel) selectionkey.channel();
					buffer.clear();
					int i = socketchannel.read(buffer);
					if (i > 0) {
						nioconnection.onDataRead(data, 0, i);
					}
				} catch (IOException ioexception1) {
					nioconnection.onClosed();
					selectionkey.cancel();
				}
			}
		}
	}

	@SuppressWarnings("unused")
	private static final int MAX_READ_BUFFER_SIZE = 1024;
	private ServerSocketChannel serverChannel;
	private ServerSocket serverSocket;
	private Selector selector;
	private SelectionKey serverKey;
	private boolean running;
	private byte data[];
	private ByteBuffer buffer;
	private ArrayList<NIOConnection> connectionList;
}
